# Multi-stage Dockerfile for {{ cookiecutter.data_app._artifact_title }} Data App
# Builds frontend and backend, then creates minimal runtime image

# NOTE on BuildKit cache mounts: The cache mounts for apt, pip, and poetry persists downloaded
# packages across builds, making subsequent installs much faster. All builds on the same host
# share the same cache mounts (e.g., across multiple data apps), so packages downloaded once
# are reused everywhere. This dramatically speeds up builds and reduces network usage.

# ============================================================================
# Stage 1: Frontend Builder
# ============================================================================
FROM node:24-alpine AS frontend-builder

WORKDIR /app/frontend

# Enable Corepack for Yarn Modern support
RUN corepack enable

# Build arguments for NPM authentication
ARG TS_NPM_VIRTUAL_REGISTRY
ARG DEPLOYMENT_ENVIRONMENT=production

# Copy ONLY dependency-related files first (for better layer caching)
# This ensures that source code changes don't invalidate the dependency installation layer
COPY frontend/package.json frontend/yarn.lock frontend/.yarnrc.yml ./

# Install all dependencies with optional NPM auth
# NOTE: Yarn 4 (Berry) ignores ~/.npmrc for authentication. Credentials must be
# written to ~/.yarnrc.yml using the `npmRegistries` / `npmScopes` format.
# `npmAuthIdent` is the base64(user:password) equivalent of the legacy `_auth` field.
RUN --mount=type=cache,target=/root/.yarn/berry/cache \
    --mount=type=secret,id=TS_NPM_VIRTUAL_AUTH \
    if [ -n "$TS_NPM_VIRTUAL_REGISTRY" ] && [ -f /run/secrets/TS_NPM_VIRTUAL_AUTH ]; then \
        AUTH_TOKEN=$(cat /run/secrets/TS_NPM_VIRTUAL_AUTH) && \
        printf 'npmRegistries:\n  "%s":\n    npmAlwaysAuth: true\n    npmAuthIdent: "%s"\nnpmScopes:\n  "tetrascience-npm":\n    npmRegistryServer: "%s"\n' \
            "$TS_NPM_VIRTUAL_REGISTRY" "$AUTH_TOKEN" "$TS_NPM_VIRTUAL_REGISTRY" > ~/.yarnrc.yml; \
    fi && \
    yarn install --immutable && \
    rm -f ~/.yarnrc.yml

# Copy source code AFTER dependencies are installed
# This ensures source changes don't invalidate the dependency layer
COPY frontend/ ./

# Build frontend — Vite outputs to '../static' (see vite.config.ts),
# which resolves to /app/static inside the builder.
RUN yarn build

# ============================================================================
# Stage 2: Python Builder
# ============================================================================
FROM python:3.14-slim AS python-builder

# Deployment environment: production|local
ARG DEPLOYMENT_ENVIRONMENT=production
# JFrog Artifactory credentials (optional)
ARG TS_PYPI_VIRTUAL_USER

WORKDIR /usr/src/app

# Install build dependencies and Poetry
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install -y --no-install-recommends \
        gcc \
        g++ \
        make \
        libffi-dev \
    && pip install --upgrade pip \
    && pip install --upgrade setuptools poetry \
    && poetry config virtualenvs.create false

# Copy Poetry configuration files from root
COPY pyproject.toml poetry.lock* ./

# Install Python dependencies with TetraScience PyPI support
RUN --mount=type=cache,target=/root/.cache/pip \
    --mount=type=cache,target=/root/.cache/pypoetry \
    --mount=type=secret,id=TS_PYPI_VIRTUAL_TOKEN \
    if [ -n "${TS_PYPI_VIRTUAL_USER}" ]; then \
        POETRY_HTTP_BASIC_TS_PYPI_VIRTUAL_USERNAME=${TS_PYPI_VIRTUAL_USER} \
        POETRY_HTTP_BASIC_TS_PYPI_VIRTUAL_PASSWORD=$(cat /run/secrets/TS_PYPI_VIRTUAL_TOKEN) \
        poetry install --only main --no-root; \
    else \
        poetry install --only main --no-root; \
    fi

# Cleanup to reduce image size (before copying to runtime stage)
RUN find /usr/local/lib/python3.14/site-packages -type d -name "tests" -exec rm -rf {} + 2>/dev/null || true \
    && find /usr/local/lib/python3.14/site-packages -type d -name "test" -exec rm -rf {} + 2>/dev/null || true \
    && find /usr/local/lib/python3.14/site-packages -name "*.so" -exec strip {} \; 2>/dev/null || true

# ============================================================================
# Stage 3: Runtime
# ============================================================================
FROM python:3.14-slim AS runtime

WORKDIR /app

# Set environment variables
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PORT=80

# Install supervisor and nginx
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
    --mount=type=cache,target=/var/lib/apt,sharing=locked \
    apt-get update && apt-get install -y --no-install-recommends \
        supervisor \
        nginx \
    && rm -rf /var/lib/apt/lists/*

# Copy Python dependencies from builder
COPY --from=python-builder /usr/local/lib/python3.14/site-packages /usr/local/lib/python3.14/site-packages
COPY --from=python-builder /usr/local/bin /usr/local/bin

# Copy backend application and install the project (no deps — already present).
# This creates console script entry points (e.g. heartbeat) in /usr/local/bin.
COPY pyproject.toml README.md ./
COPY backend/ ./backend/
RUN pip install --no-deps .

# Copy the built frontend static files
COPY --from=frontend-builder /app/static ./static

# Conditionally copy supervisord config based on deployment environment
COPY conf/ /tmp/conf/
COPY conf/nginx.conf /etc/nginx/nginx.conf

ARG DEPLOYMENT_ENVIRONMENT=production

RUN mkdir -p /etc/supervisor/conf.d \
    && if [ "$DEPLOYMENT_ENVIRONMENT" = "production" ]; then \
        cp /tmp/conf/supervisord.conf /etc/supervisor/conf.d/supervisord.conf; \
    elif [ "$DEPLOYMENT_ENVIRONMENT" = "local" ]; then \
        cp /tmp/conf/supervisord-local.conf /etc/supervisor/conf.d/supervisord.conf; \
    else \
        echo "Invalid DEPLOYMENT_ENVIRONMENT: $DEPLOYMENT_ENVIRONMENT"; \
        exit 1; \
    fi \
    && rm -rf /tmp/conf/

# Copy and set up entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

# Expose Nginx
EXPOSE 80

# Run entrypoint script which displays startup info and starts supervisord
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
